using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

using ViewOnCore.HostContract.Media;
using ViewOnCore.Helper.Debug;
using ViewOnCore.Device;
using ViewOnCore.HostContract;

using Un4seen.Bass;
using Un4seen.Bass.Misc;
using Un4seen.Bass.AddOn.Fx;
using Un4seen.Bass.AddOn.Tags;
using Un4seen.Bass.AddOn.Cd;
using Un4seen.Bass.AddOn.Wa;
using Un4seen.Bass.AddOn.Mix;

using RocketDivision.StarBurnX;

namespace ViewOn.AddInsHostContract.Burning
{

    /// <summary>
    /// We use the StarBurn SDK : http://www.rocketdivision.com/burn_sdk.html
    /// </summary>
    sealed public class BurningService : IBurningService
    {

        #region Variables

        private bool TEST_MODE = false; // Simulate burning

        private bool _hasBurningDevice;

        private StarBurnX _starBurnX;
        private AudioBurner _audioBurner;
        private DataBurner _dataBurner;

        private BurningMode _burningMode = BurningMode.Audio;
        private BurningStep _burningStep;
        private bool _isCanceled = false;
        private int _totalDuration;

        List<string> _trackToClean = new List<string>();

        //---- Events
        public event BurningServiceStatusEventHandler BurningStatusChanged;
        public event BurningServiceProgressEventHandler BurningProgressChanged;
        public event BurningServiceErrorEventHandler BurningErrorOccured;

        #endregion

        #region Constructor

        public BurningService()
        {
            //---- Initialize
            try
            {
                // Create new instance of component
                _starBurnX = new StarBurnX();

                // Initialize component with the appropriate license.
                // Note that we use the "Free" license type for our example.
                string licenceKey = "7d3u38ABXdnCg4qyloaRlLCon6aYnqSmh7OapJSK6PuhrOemcZsb1EzjKdK81QqO19y64b3apvyppq6V0yglZ7AWpiKaPLMAQzMfPekFEXl0WPkN0QLAeo0taDaBeCws6w7zC5RuSUnUGDZ5LQIQZp0Ol1cmK8Y3nDrbJNInsWy6N9c++VzaC88fVyvxRcEwohJXaF1dpGkHTjNEsjbucAwBLxusOJBoVj+WZ14m4GyFLAsD1nVELGYi0gQr1L63mPIVtJ2tduPJrVa94cNoxaGJWJQA6lK/ebE0xUHondSJq1z73oCnptPEMswft5T4BtmP/4LmSYTq6APf/tEl7a3DEPWO+p71V/qFwcGPX8F6ybT2lsgjyDj06q+OhFG6dd1y6HWAFoFn6HK3McrE2421mYF1gwyfotwOqjs3HWFTLPdN2TF6W6NxTFVZHX0ZlVriNDRhf3RIBu8XiSJjfOlSej3VC5V2+gm+Al5UB09iEFpaE12mDDcjQjUiDR4HFEusJfBnQ3qKK5sB7hSbR3plnAwkb5whkFvKOpJw6nNddiJxNmFOUTNE/iNwNHUT7EUiGKQaXkXdR4F/wIYBnBTzjsGBy9CMU49KiA/UEO6Q2VjanIbVtbnoK5T2gP/qSJCDluT7ZpOL5BqMLqDnkvH/ycTUvhzqJ9Ta/9+V/bEp0rTDj5wa1kSdydr8rDSAXKPIldeQr54Oj5Gc3NY5ykS6fdyk5QetFZD62+C09NLmyu/EGJcJ5HKvTIxhMOsuCR7wJzI3eCjfHoclBVFgOdYwehJ5RkREjQzOBuw9UEXqRtAMQF9IT8cYhTvObHVnvmqcWjpGvHF6Xak2EUKDOwxbaERmHyNxbAheH2pm7BvpcQgAWjksKzgrIB/uHp01KV+UDY5acm2rLyURQS8BZ3Nl6ANgSM58Rl/XStSOG76slpWRoJLysnCyfb64/y+qnpSIiifHDrR0tPjUCqH4xAf7oOE5xZ3e2aZB0H/asP3+3Lu4VejGyw/nd+IY863ZY+sYnjqH3fCjp4r3q6iR68z12//A+8X2z/fL58PJ/8PH/e6oArVv5taEQJXOk9ugF9LtsHrIrcnZ3BLig2qRI71/p1YaPr0F4Qv6Y+xguhcuJ8JPRmpBV1148zkRdvV+4wQ9HOtagnseN4x7fT8ae/FORTyRXKpEZx0WGzh8PhTgQRtDmxR6PZ0zYB1iWrRb+HKrLAxqUmbffn0KiENoE3BZjRTyfB5JOT2TSMxyA2UNb6UKGC+VRoYqxDwHnoyLorusiXG5y7Os9HfVIKzEx0P5b8yd3V6pWcEtvP37qLjZ/7WBqZNKkLuPrs0IxrHY4t4bwg7Zp82ip8zcsKy00sye380=";

                _starBurnX.Initialize(licenceKey);
            }
            catch (Exception e)
            {
                LogManager.Handle(e);
                _hasBurningDevice = false;
                return;
            }

            _audioBurner = new AudioBurnerClass();
            _audioBurner.OnProgress += new IAudioBurnerEvents_OnProgressEventHandler(OnProgress);
            _audioBurner.OnBuffer += new IAudioBurnerEvents_OnBufferEventHandler(OnBurningBuffer);

            _dataBurner = new DataBurnerClass();
            _dataBurner.OnProgress += new IDataBurnerEvents_OnProgressEventHandler(OnProgress);
            _dataBurner.OnBuffer += new IDataBurnerEvents_OnBufferEventHandler(OnBurningBuffer);
            _dataBurner.OnNameCollision += new IDataBurnerEvents_OnNameCollisionEventHandler(OnNameCollision);

            //----
            foreach (Drive drive in _starBurnX.Drives)
            {
                DriveInfo driveInfo = drive.DriveInfo;

                // Register only specified devices
                if ((driveInfo.SupportedMediaWrite & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_CD & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_RECORDABLE) != 0)
                {
                    _hasBurningDevice = true;
                    break;
                }
            }
        }

        #endregion

        #region Properties

        public BurningMode BurningMode
        {
            get
            {
                return _burningMode;
            }
            set
            {
                _burningMode = value;
            }
        }

        public bool HasBurningDevice
        {
            get
            {
                return _hasBurningDevice;
            }
        }

        public List<BurningDriveInfo> BurningDevices
        {
            get
            {
                List<BurningDriveInfo> drives = new List<BurningDriveInfo>();

                foreach (Drive drive in _starBurnX.Drives)
                {
                    DriveInfo driveInfo = drive.DriveInfo;

                    // Register only specified devices
                    if ((driveInfo.SupportedMediaWrite & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_CD & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_RECORDABLE) != 0)
                        drives.Add(new ConcreteBurningDriveInfo(drive));
                }

                return drives;
            }
        }

        public string TotalDuration
        {
            get
            {
                return FormatDurationTime(_totalDuration);
            }
        }

        private string FormatDurationTime(long pSeconds)
        {
            if (pSeconds <= 0) { return " 00:00 "; }
            if (pSeconds < 60) { return " 00:" + pSeconds + " "; }

            long sec;
            long min;
            long hour;

            if (pSeconds < 3600)
            {
                min = (pSeconds / 60);
                sec = pSeconds - min * 60;

                return String.Format(" {0:00}:{1:00} ", min, sec);
            }
            long usedSecs = 0;
            hour = pSeconds / 60 / 60;
            usedSecs += hour * 60 * 60;

            min = (pSeconds - usedSecs) / 60;
            usedSecs = usedSecs + min * 60;

            sec = pSeconds - usedSecs;
            return String.Format(" {0:00}:{1:00}:{2:00} ", hour, min, sec);
        }

        #endregion

        #region Refresh

        public void Refresh()
        {
            _starBurnX.Refresh();
        }

        #endregion

        #region Burn

        public void Burn(List<MediaData> _medias, BurningDriveInfo driveInfo, BurningDriveSpeed burningSpeed, bool verify)
        {
            Drive drive = ((ConcreteBurningDriveInfo)driveInfo).Drive;

            DriveSpeed writeSpeed = null;
            foreach (DriveSpeed speed in drive.DriveInfo.SupportedWriteSpeeds)
                if (writeSpeed.Speed == burningSpeed.Speed)
                {
                    writeSpeed = speed;
                    break;
                }

            if (drive == null)
                return;

            if (_burningMode == BurningMode.Audio)
            {
                BurnAudio(_medias, drive, writeSpeed);
                return;
            }

            BurnData(_medias, drive, writeSpeed, verify);
        }

        #endregion

        #region BurnData

        private void BurnData(List<MediaData> _medias, Drive drive, DriveSpeed writeSpeed, bool verify)
        {
            _isCanceled = false;
            _totalDuration = 0;
            _burningStep = BurningStep.Preparing;
            _trackToClean.Clear();

            //---- Set up burner
            // Set the selected drive
            _dataBurner.Drive = drive;

            // Set the selected write speed
            _dataBurner.WriteSpeed = writeSpeed.Speed;

            //---- Set up CD Label
            //_dataBurner.Name = "";

            //---- Multi session ?
            _dataBurner.NextSessionAllowed = true;

            //---- Add all the files
            _dataBurner.Files.Clear();
            for (int index = 0; index < _medias.Count; index++)
            {
                _dataBurner.AddFile(_medias[index].Path);
                if (_isCanceled)
                {
                    OnBurningStop(true);
                    return;
                }
            }

            //---- Burn...
            _burningStep = BurningStep.Burning;

            // Set the selected write mode
            _dataBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_TAO;
            //switch (cmbWriteModes.Text)
            //{
            //    case "Auto":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_AUTO;
            //        break;
            //    case "Track-At-Once":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_TAO;
            //        break;
            //    case "Session-At-Once":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_SAO;
            //        break;
            //    case "Disc-At-Once PQ":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_DAO_16;
            //        break;
            //    case "Disc-At-Once Raw PW":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_DAO_96;
            //        break;
            //}

            _dataBurner.LockTray = true;

            try
            {
                ChangeBurningStatus("Burning, please wait...");
                _dataBurner.Burn(TEST_MODE, "", "", "");
            }
            catch (Exception e)
            {
                LogManager.Handle(e);

                OnBurningStop(false);

                if (BurningErrorOccured != null)
                    BurningErrorOccured("Cannot burn due to a generic error");
                return;
            }
            finally
            {
                if (!verify)
                    OnBurningStop(false);
            }

            //---- Verify
            if (verify)
                try
                {
                    ChangeBurningStatus("Start verification, please wait...");
                    _dataBurner.Verify();
                }
                catch (Exception e2)
                {
                    LogManager.Handle(e2);
                    if (BurningErrorOccured != null)
                        BurningErrorOccured("Error during verification");
                }
                finally
                {
                    OnVerifyStop(false);
                }
        }

        #endregion

        #region BurnAudio

        private void BurnAudio(List<MediaData> _medias, Drive drive, DriveSpeed writeSpeed)
        {
            _isCanceled = false;
            _totalDuration = 0;
            _burningStep = BurningStep.Preparing;
            _trackToClean.Clear();

            //---- Set up burner
            // Set the selected drive
            _audioBurner.Drive = drive;

            // Set the selected write speed
            _audioBurner.WriteSpeed = writeSpeed.Speed;

            //---- Set up CD Title
            _audioBurner.Artist = "";
            _audioBurner.Title = "";

            //---- Add all the files
            _audioBurner.Tracks.Clear();
            for (int index = 0; index < _medias.Count; index++)
            {
                EncodeFileToCDA(_medias[index], index);
                if (_isCanceled)
                {
                    OnBurningStop(true);
                    return;
                }
            }

            //---- Burn...
            _burningStep = BurningStep.Burning;

            //--- Set the selected write mode
            _audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_AUTO;
            //switch (cmbWriteModes.Text)
            //{
            //    case "Auto":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_AUTO;
            //        break;
            //    case "Track-At-Once":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_TAO;
            //        break;
            //    case "Session-At-Once":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_SAO;
            //        break;
            //    case "Disc-At-Once PQ":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_DAO_16;
            //        break;
            //    case "Disc-At-Once Raw PW":
            //        m_audioBurner.Mode = STARBURN_WRITE_MODE.STARBURN_WRITE_MODE_DAO_96;
            //        break;
            //}

            _audioBurner.LockTray = true;

            try
            {
                ChangeBurningStatus("Start burning process, please wait...");
                _audioBurner.Burn(TEST_MODE);
            }
            catch (Exception e)
            {
                LogManager.Handle(e);

                OnBurningStop(false);

                if (BurningErrorOccured != null)
                    BurningErrorOccured("Cannot burn due to a generic error");

                return;
            }
            finally
            {
                OnBurningStop(false);
            }
        }

        private void EncodeFileToCDA(MediaData media, int index)
        {
            ChangeBurningStatus("Preparing : " + media.Title);

            //---- Convert to WAV
            AudioTrack track = null;
            if (media.Path.EndsWith(".wav") || media.Path.EndsWith(".wma"))
            {
                track = _audioBurner.AddFile(media.Path);
                _totalDuration += track.Duration;
            }
            else
            {
                string wavFilePath = Host.SettingsService.CachePath + "cdburn$vo$" + index + ".cda";

                _trackToClean.Add(wavFilePath);

                if (!EncodeToCDA(media.Path, wavFilePath))
                    return;

                //---- Add the track
                track = _audioBurner.AddFile(wavFilePath);
                _totalDuration += track.Duration;
            }

            Music music = media as Music;
            track.Artist = music.Artist;
            track.Title = music.Title;
        }

        private void CleanTempFiles()
        {
            foreach (string path in _trackToClean)
                try
                {
                    System.IO.File.Delete(path);
                }
                catch
                {
                }

            _trackToClean.Clear();
        }

        #endregion

        #region CancelBurn

        public void CancelBurn()
        {
            _isCanceled = true;
            ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncCancel));
        }

        private void AsyncCancel(object state)
        {
            try
            {
                _audioBurner.Cancel();
            }
            catch
            {
            }
        }

        #endregion

        #region ValidateBurningData

        public string ValidateBurningData(BurningDriveInfo burningDriveInfo)
        {
            // Whether drive is specified
            if (burningDriveInfo == null)
                return "Drive is not specified";

            Drive drive = ((ConcreteBurningDriveInfo)burningDriveInfo).Drive;

            //// Validate, whether data for burning are present
            //if (_audioBurner.Tracks.Count == 0)
            //    return "No tracks specified for burning";

            // Whether drive is ready to burn
            if (drive.State != STARBURN_DRIVE_STATE.STARBURN_DRIVE_STATE_READY)
                return "Drive is not ready to burn";

            // Whether drive is a recordable CD drive
            DriveInfo driveInfo = drive.DriveInfo;
            if ((driveInfo.SupportedMediaWrite & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_CD) == 0)
                return "Drive is not recordable CD drive";

            // Whether disc is a recordable CD disc
            DiscInfo discInfo = drive.DiscInfo;
            if ((discInfo.DiscType & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_CD & (int)STARBURN_DISC_TYPE.STARBURN_DISC_TYPE_RECORDABLE) == 0)
                return "Disc is not recordable CD disc";

            // Whether disc is empty
            if (discInfo.Status != STARBURN_DISC_STATUS.STARBURN_DISC_STATUS_EMPTY)
                return "Burning can not be performed on current disc because disc is not empty";

            // Verify, whether disc has a free space to write tracks
            if (_burningMode == BurningMode.Audio)
            {
                int AUDIO_SEC_PER_TRACK = 75;
                if (((discInfo.TotalSize - discInfo.UsedSize) / AUDIO_SEC_PER_TRACK) < _totalDuration)
                    return "The disc has no free space!";
            }
            else
            {
                long curSessionSize = 0;
                foreach (IDataFile file in _dataBurner.Files)
                    curSessionSize += file.Size;

                // Check, whether free space is available
                if (ConvertLB2Kb(discInfo.TotalSize - discInfo.UsedSize) < curSessionSize)
                    return "The disc has no free space!";
            }

            return null;
        }

        private long ConvertLB2Kb(long lbValue)
        {
            // This constant is used to calculate size of the Data track (in kb.)
            int MODE1_LB_SIZE = 2048;
            if (lbValue > 0)
            {
                double t;
                t = (lbValue / (1024));
                return (long)(t * MODE1_LB_SIZE);
            }

            return 0;
        }

        #endregion

        #region EncodeToCDA

        private bool EncodeToCDA(string sourcePath, string targetPath)
        {
            bool isOk = true;

            //---- Open the input file
            int inputStream = Bass.BASS_StreamCreateFile(sourcePath, 0, 0, BASSStream.BASS_STREAM_DECODE | BASSStream.BASS_SAMPLE_FLOAT);

            //---- Create the mixer streal
            int mixerStream = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSStream.BASS_STREAM_DECODE | BASSStream.BASS_SAMPLE_FLOAT);

            //--- Plug source into the mixer (just to be sure, we use the downmix option to stereo)
            bool okA = BassMix.BASS_Mixer_StreamAddChannel(mixerStream, inputStream, (int)BASSStream.BASS_MIXER_DOWNMIX);

            //---- CDA files are basically standard wav files with 44100Hz, 16-bit, stereo resolution.
            EncoderWAV encoder = new EncoderWAV(mixerStream);
            encoder.WAV_BitsPerSample = 16; // 16-bit
            encoder.OutputFile = targetPath;

            encoder.Start(null, 0);

            // decode the stream (if not using a decoding channel, simply call "Bass.BASS_ChannelPlay" here)
            byte[] encBuffer = new byte[65536]; // our dummy encoder buffer
            int index = 0;
            int len = 1;
            while (len > 0)
            {
                // getting sample data will automatically feed the encoder
                len = Bass.BASS_ChannelGetData(mixerStream, encBuffer, encBuffer.Length);

                if (index % 10 == 0)
                    System.Threading.Thread.Sleep(1);

                index++;

                if (_isCanceled)
                {
                    isOk = false;
                    break;
                }
            }
            encoder.Stop();  // finish

            //---- Clean
            Bass.BASS_StreamFree(mixerStream);
            Bass.BASS_StreamFree(inputStream);

            return isOk;
        }

        #endregion

        #region ChangeBurningStatus

        private void ChangeBurningStatus(string message)
        {
            if (BurningStatusChanged != null)
                BurningStatusChanged(message);
        }

        #endregion

        #region OnBurningStop

        private void OnBurningStop(bool canceledByUsed)
        {
            if (_burningMode == BurningMode.Audio)
            {
                OnBurningAudioStop(canceledByUsed);
                return;
            }

            OnBurningDataStop(canceledByUsed);
        }

        #endregion

        #region OnBurningAudioStop

        private void OnBurningAudioStop(bool canceledByUsed)
        {
            _audioBurner.LockTray = false;
            _audioBurner.Drive.Eject();
            CleanTempFiles();

            //---- After cancelation
            if (canceledByUsed)
            {
                ChangeBurningStatus("Burning of the disc has been canceled by user.");
                return;
            }

            //---- Burning ended
            if (_audioBurner.LastErrorCode != STARBURN_RESULT_CODES.STARBURN_OPERATION_SUCCEEDED)
            {
                if (_audioBurner.LastErrorCode == STARBURN_RESULT_CODES.STARBURN_ERR_DISC_FINALIZED)
                    ChangeBurningStatus("Burning operation can not be performed because disc is finalized!");
                else
                    ChangeBurningStatus("Burning operation can not be performed - unknown error!");
                return;
            }

            ChangeBurningStatus("Disc has been successfully burned.");
        }

        #endregion

        #region OnBurningDataStop

        private void OnBurningDataStop(bool canceledByUsed)
        {
            _dataBurner.LockTray = false;
            _dataBurner.Drive.Eject();

            //---- After cancelation
            if (canceledByUsed)
            {
                ChangeBurningStatus("Burning of the disc has been canceled by user.");
                return;
            }

            //---- Burning ended
            if (_dataBurner.LastErrorCode != STARBURN_RESULT_CODES.STARBURN_OPERATION_SUCCEEDED)
            {
                if (_dataBurner.LastErrorCode == STARBURN_RESULT_CODES.STARBURN_ERR_DISC_FINALIZED)
                    ChangeBurningStatus("Burning operation can not be performed because disc is finalized!");
                else
                    ChangeBurningStatus("Burning operation can not be performed - unknown error!");
                return;
            }

            ChangeBurningStatus("Disc has been successfully burned.");
        }

        #endregion

        #region OnVerifyStop

        private void OnVerifyStop(bool canceledByUsed)
        {
            _dataBurner.LockTray = false;
            _dataBurner.Drive.Eject();

            //---- After cancelation
            if (canceledByUsed)
            {
                ChangeBurningStatus("Verification of the disc has been canceled by user.");
                return;
            }

            //---- Burning ended
            if (_dataBurner.LastErrorCode != STARBURN_RESULT_CODES.STARBURN_OPERATION_SUCCEEDED)
            {
                ChangeBurningStatus("Verification process has been failed!");
                return;
            }

            ChangeBurningStatus("Verification complete! Disc has been successfully burned.");
        }

        #endregion

        #region On...

        private void OnProgress(int percent, int timeRemaining)
        {
            if (BurningProgressChanged != null)
                BurningProgressChanged(_burningStep, percent, timeRemaining);
        }

        private void OnBurningBuffer(int percent)
        {
            if (BurningProgressChanged != null)
                BurningProgressChanged(_burningStep, percent, -1);
        }

        STARBURN_NAME_COLLISION_SOLUTION OnNameCollision(string ExistingFilePath, string AddingFilePath, ref string AddingFileName)
        {
            return STARBURN_NAME_COLLISION_SOLUTION.STARBURN_NAME_COLLISION_RENAME_NEW;
        }

        #endregion

        #region Helpers

        public string ConvertLBytesToTime(Int64 LBCount)
        {
            int AUDIO_LB_SEC = 75;      // This constant is used to estimate time of the Audio track
            string spaceValue;

            if (LBCount <= 0)
            {
                spaceValue = "00:00 sec";
            }
            else
            {
                LBCount /= AUDIO_LB_SEC;
                spaceValue = String.Format("{0:##00}:{1:00} min", LBCount / 60, LBCount % 60);
            }

            return spaceValue;
        }

        public TimeSpan ConvertToTime(long LBCount)
        {
            int AUDIO_LB_SEC = 75;      // This constant is used to estimate time of the Audio track

            if (LBCount <= 0)
                return TimeSpan.Zero;

            LBCount /= AUDIO_LB_SEC;
            return new TimeSpan(LBCount * TimeSpan.TicksPerSecond);
        }

        //public string ConvertBytesToMbytes(Int64 LBCount)
        //{
        //    int MODE1_LB_SIZE = 2048;    // This constant is used to calculate size of the Data track (in kb.)
        //    string spaceValue;

        //    if (LBCount <= 0)
        //    {
        //        spaceValue = " 0 MBytes";
        //    }
        //    else
        //    {
        //        spaceValue = String.Format("{0:#0.00} MBytes", (LBCount * MODE1_LB_SIZE) / (1024 * 1024));
        //    }

        //    return spaceValue;
        //}

        public long ConvertToBytes(long LBCount)
        {
            // This constant is used to calculate size of the Data track (in kb.)
            int MODE1_LB_SIZE = 2048;

            return LBCount * MODE1_LB_SIZE;
        }

        #endregion

    }

    #region ConcreteBurningDriveInfo

    /// <summary>
    /// A concrete implementation of the class 'BurningDriveInfo'.
    /// </summary>
    sealed internal class ConcreteBurningDriveInfo : BurningDriveInfo
    {
        internal Drive Drive;
        private List<BurningDriveSpeed> _supportedWriteSpeeds = null;

        internal ConcreteBurningDriveInfo(Drive drive)
        {
            this.Drive = drive;
            Letter = Drive.DriveInfo.Letter;
            Vendor = Drive.DriveInfo.Vendor;
            Product = Drive.DriveInfo.Product;
            DiscTotalSize = Drive.DiscInfo.TotalSize;

            WriteSpeed = Drive.DriveInfo.WriteSpeed;
        }

        override public List<BurningDriveSpeed> SupportedWriteSpeeds
        {
            get
            {
                if (_supportedWriteSpeeds == null)
                {
                    _supportedWriteSpeeds = new List<BurningDriveSpeed>();
                    foreach (DriveSpeed speed in Drive.DriveInfo.SupportedWriteSpeeds)
                        _supportedWriteSpeeds.Add(new BurningDriveSpeed(speed.Speed, speed.SpeedRatio));
                }
                return _supportedWriteSpeeds;
            }
        }
    }

    #endregion

}
